package com.de.mybatis.autosql;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 *  
 *  * @projectName RuoYi
 *  * @title     AutoSqlUtil   
 *  * @package    com.ruoyi.common.utils.sql  
 *  * @description    自动拼接sql条件的工具类 
 *  * @author IT_CREAT     
 *  * @date  2019 2019/9/10 14:33  
 *  * @version V1.0.0 
 *  
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@ToString
public class AutoSqlStruct<T> {

    /**
     * 查询条件参数类型
     */
    public final static String eq = "=";//等于
    public final static String ne = "!=";//不等于
    public final static String lt = "<";//小于
    public final static String gt = ">";//大于
    public final static String ltAndEq = "<=";//小于等于
    public final static String gtAndEq = ">=";//大于等于
    public final static String in = "in";//in
    public final static String leftLike = "left like";//左模糊查询
    public final static String rightLike = "right like";//右模糊查询
    public final static String leftAndRightLike = "leftAndRight like";//两边模糊查询
    public final static String between = "between";//范围查询

    /**
     * 排除参数类型
     */
    public final static String desc = "desc";//降序排序
    public final static String asc = "asc";//升序排序

    /**
     * 条件拼接类型
     */
    public final static String and = "and";//and拼接条件
    public final static String or = "or";//or拼接条件

    /**
     * 拼装where条件
     */
    private Map<String, Object> whereColumn;
    /**
     * 拼装排序条件
     */
    private Map<String, String> orderColumn;

    /**
     * 需要修改的数据库的字段信息集合
     */
    private Map<String, Object> upDbData;

    /**
     * 对普通list集合进行链式构造
     */
    private List<Object> list;

    private Class<T> clazz;

    public AutoSqlStruct(Class<T> clazz) {
        this.clazz = clazz;
    }

    public static AutoSqlStruct create() {
        return new AutoSqlStruct();
    }

    /**
     * 对list集合进行封装，实现链式调用
     *
     * @param object 向集合中加入的值
     * @return 本工具类
     */
    public AutoSqlStruct addList(Object object) {
        if (CollectionUtils.isEmpty(this.list)) {
            this.list = new ArrayList<>();
        }
        List<Object> list = this.list;
        list.add(object);
        return this;
    }

    /**
     * 得到构造好的between条件
     *
     * @param start 起始值
     * @param end   结束值
     * @return 集合
     */
    public static List getBetweenAndList(Object start, Object end) {
        if (ObjectUtils.isEmpty(start) || ObjectUtils.isEmpty(end)) {
            return null;
        }
        ArrayList<Object> objects = new ArrayList<>();
        objects.add(0, start);
        objects.add(1, end);
        return objects;
    }

    /**
     * 追加where条件
     *
     * @param columnName  数据库字段名称
     * @param whereSymbol 查询条件符号 = like 等,如果whereSymbol条件是in,传入值columnValue必须是list集合
     * @param columnValue 对应的查询条件字段值
     * @param spliceType  每个条件之间拼接类型，and or ,如果为空，默认以and拼接
     * @return 返回本工具对象，实现链式写法
     */
    public AutoSqlStruct addWhereColumn(String columnName, String whereSymbol, Object columnValue, String spliceType) {
        if (StringUtils.isBlank(columnName) || StringUtils.isBlank(whereSymbol) || ObjectUtils.isEmpty(columnValue)) {
            return this;
        }
        if (columnValue instanceof String && StringUtils.isBlank((String) columnValue)) {
            return this;
        }
        if (ObjectUtils.isEmpty(this.whereColumn)) {
            this.whereColumn = new LinkedHashMap<>();
        }
        String spliceTypeCopy = "and";
        if (!StringUtils.isBlank(spliceType)) {
            spliceTypeCopy = spliceType;
        }
        Map<String, Object> whereColumn = this.whereColumn;
        String value;
        if (AutoSqlStruct.leftLike.equals(whereSymbol)) {
            value = spliceTypeCopy + "@" + columnName + " like concat('%','" + columnValue + "')";
            whereColumn.put("asm_null_" + whereColumn.size(), value);
        } else if (AutoSqlStruct.rightLike.equals(whereSymbol)) {
            value = spliceTypeCopy + "@" + columnName + " like concat('" + columnValue + "','%')";
            whereColumn.put("asm_null_" + whereColumn.size(), value);
        } else if (AutoSqlStruct.leftAndRightLike.equals(whereSymbol)) {
            value = spliceTypeCopy + "@" + columnName + " like concat('%','" + columnValue + "','%')";
            whereColumn.put("asm_null_" + whereColumn.size(), value);
        } else if (AutoSqlStruct.in.equals(whereSymbol)) {
            if (columnValue instanceof List) {
                String s = array2StrAutoSql((List<Object>) columnValue, ",");
                value = spliceTypeCopy + "@" + columnName + " in(" + s + ")";
                whereColumn.put("asm_null_" + whereColumn.size(), value);
            }
        } else if (AutoSqlStruct.between.equals(whereSymbol)) {
            if (columnValue instanceof List) {
                List<Object> objects = (List<Object>) columnValue;
                if (objects.get(0) instanceof String) {
                    value = spliceTypeCopy + "@" + columnName + " between '" + objects.get(0) + "' and '" + objects.get(1) + "'";
                } else {
                    value = spliceTypeCopy + "@" + columnName + " between " + objects.get(0) + " and " + objects.get(1);
                }
                whereColumn.put("asm_null_" + whereColumn.size(), value);
            }
        } else {
            String key = spliceTypeCopy + "@" + columnName + "@" + whereSymbol;
            if (whereColumn.containsKey(key)) {
                Integer t = 1;
                while (true) {
                    key = spliceTypeCopy + addSpaceLeft("@", t) + columnName + "@" + whereSymbol;
                    if (!whereColumn.containsKey(key)) {
                        break;
                    }
                    t++;
                }
            }
            whereColumn.put(key, columnValue);
        }
        return this;
    }

    /**
     * 得到limit限制条件语句
     *
     * @param pageIndex 起始页的页码（最小为1，也就是第一页）
     * @param rows      每页需要查询的数据条数
     * @return limit 限制语句
     */
    public String getLimitColumn(Long pageIndex, Long rows) {
        if (ObjectUtils.isEmpty(pageIndex) || ObjectUtils.isEmpty(rows)) {
            return null;
        }
        //计算数据获取的起始位置
        long index = (pageIndex - 1) * rows;
        return index + "," + rows;
    }

    /**
     * 追加排序条件
     *
     * @param columnName 数据库中字段名称
     * @param orderType  排序的类型，asc和desc
     * @return 本工具类，实现链式写法
     */
    public AutoSqlStruct addOrderColumn(String columnName, String orderType) {
        if (StringUtils.isBlank(columnName) || StringUtils.isBlank(orderType)) {
            return this;
        }
        if (ObjectUtils.isEmpty(this.orderColumn)) {
            this.orderColumn = new LinkedHashMap<>();
        }
        Map<String, String> orderColumn = this.orderColumn;
        orderColumn.put(columnName, orderType);
        return this;
    }

    /**
     * 添加需要修改的字段信息
     *
     * @param columnName  数据库字段名称
     * @param columnValue 数据库字段值
     * @return 本工具类，实现链式调用
     */
    public AutoSqlStruct addUpdateDbData(String columnName, Object columnValue) {
        if (StringUtils.isBlank(columnName) || ObjectUtils.isEmpty(columnValue)) {
            return this;
        }
        if (ObjectUtils.isEmpty(this.upDbData)) {
            this.upDbData = new LinkedHashMap<>();
        }
        Map<String, Object> upDbData = this.upDbData;
        upDbData.put(columnName, columnValue);
        return this;
    }

    private String addSpaceLeft(String str, Integer count) {
        if (ObjectUtils.isEmpty(count) || count <= 0) {
            return str;
        }
        StringBuilder strCopy = new StringBuilder(str);
        for (int i = 0; i < count; i++) {
            strCopy.insert(0, " ");
        }
        return strCopy.toString();
    }

    public String array2StrAutoSql(List<Object> strings, String chart) {
        StringBuilder strReturn = new StringBuilder();
        if (!CollectionUtils.isEmpty(strings)) {
            int sign = 0;
            for (Object str : strings) {
                if (sign == 0) {
                    if (str instanceof String) {
                        strReturn.append("'").append(str).append("'");
                    } else {
                        strReturn.append(str);
                    }
                } else {
                    if (str instanceof String) {
                        strReturn.append(chart).append("'").append(str).append("'");
                    } else {
                        strReturn.append(chart).append(str);
                    }
                }
                sign++;
            }
        }
        return strReturn.toString();
    }

    /**
     * 测试方法
     *
     * @param args 普通参数
     */
    public static void main(String[] args) {

        //创建工具类实例
        AutoSqlStruct autoSqlStruct = AutoSqlStruct.create();


        //添加where条件并得到where条件map集合
        Map whereColumn = autoSqlStruct.addWhereColumn("name", AutoSqlStruct.leftAndRightLike, "测试名称撒", null)
                .addWhereColumn("age", AutoSqlStruct.eq, 15, null)
                .addWhereColumn("id", AutoSqlStruct.in, autoSqlStruct.addList(15)
                        .addList(16)
                        .addList(22)
                        .getList(), AutoSqlStruct.or)
                .addWhereColumn("hobbi", AutoSqlStruct.between, AutoSqlStruct.getBetweenAndList("打篮球", "打羽毛球"), null)
                .getWhereColumn();
        System.out.println(whereColumn);

        //添加并得到排序条件map集合
        Map orderColumn = autoSqlStruct.addOrderColumn("name", AutoSqlStruct.desc)
                .addOrderColumn("age", AutoSqlStruct.asc)
                .getOrderColumn();
        System.out.println(orderColumn);

        //得到limit限制条件语句
        String limitColumn = autoSqlStruct.getLimitColumn(1L, 6L);
        System.out.println(limitColumn);

        //添加需要修改的字段值并得到map集合
        Map upDbData = autoSqlStruct.addUpdateDbData("name", "张三")
                .addUpdateDbData("age", 19)
                .getUpDbData();
        System.out.println(upDbData);
    }


}
